/**
 * BrandBigData.com Inc.
 * Copyright (c) 2016 All Rights Reserved.
 */
package com.qibao.toolkit;

import com.qibao.toolkit.exception.PythonExecuteException;
import com.qibao.toolkit.facade.ExecuteContext;
import com.qibao.toolkit.util.JsonUtils;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @author xumin
 * @version $Id:PythonExecutor.java, v0.1 2017/7/5 9:11 xumin
 */
public class PythonExecutor {
    private static final Logger logger = LoggerFactory.getLogger(PythonExecutor.class);
    private static final String host = "localhost";
    private int port;
    private Process process;
    private File pythonFile;

    public PythonExecutor(int port) {
        this.port = port;
        startPythonService();
    }

    public String executeScript(String script, ExecuteContext executeContext) throws PythonExecuteException {
        File tempFile = null;
        try {
            tempFile = File.createTempFile("plutus", "python");
            FileUtils.write(tempFile, script, "utf-8");
            BasicNameValuePair file = new BasicNameValuePair("file", tempFile.getAbsolutePath());
            BasicNameValuePair variables = new BasicNameValuePair("variables", JsonUtils.writeValueAsString(executeContext.getParameters()));
            logger.info("传递给python的variables入参：{}", variables.getValue());
            HttpEntity httpEntity = new UrlEncodedFormEntity(Lists.newArrayList(file, variables), Charsets.UTF_8);
            Response response = Request.Post("http://" + host + ":" + port).body(httpEntity).execute();
            ExecuteResponse executeResponse = JsonUtils.readValue(response.returnContent().asString(), ExecuteResponse.class);
            if (!executeResponse.isSuccess()) {
                throw new PythonExecuteException(executeResponse.getErrorMsg());
            }
            logger.info("python脚本执行结果：" + executeResponse.getResult());
            return executeResponse.getResult();
        } catch (Exception e) {
            logger.error("python脚本执行出错,脚本为：\n{}\n入参为:{}", script, JsonUtils.writeValueAsString(executeContext.getParameters()));
            throw new PythonExecuteException(e.getMessage(), e);
        } finally {
            if (tempFile != null) {
                tempFile.delete();
            }
        }
    }

    public void close() {
        logger.warn("关闭python服务进程 port:" + port);
        if (process.isAlive()) {
            process.destroy();
        }
        pythonFile.delete();
    }

    public void startPythonService() {
        try {
            pythonFile = File.createTempFile("plutus", "python-" + port);
            FileUtils.copyInputStreamToFile(ResourceUtils.getURL("classpath:WebServer.py").openStream(), pythonFile);
            ProcessBuilder processBuilder = new ProcessBuilder();
            processBuilder.inheritIO();
            String OSName = System.getProperty("os.name");
            String python = "python2.7";
            if (OSName != null && OSName.toLowerCase().startsWith("win")) {
                python = "python";
            }
            process = processBuilder.command(python, pythonFile.getAbsolutePath(), "runserver", "--port", port + "").start();
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                close();
            }));

            for (int i = 0; i < 10; i++) {
                TimeUnit.SECONDS.sleep(1);
                if (isAlive()) {
                    logger.warn("成功启动python服务进程 port:" + port);
                    return;
                }
            }
            throw new Exception("启动python服务进程失败 port:" + port);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }

    }

    public boolean isAlive() {
        try {
            boolean isAlive = process.isAlive() && Request.Get("http://localhost:" + port).execute().returnResponse().getStatusLine().getStatusCode() == HttpStatus.SC_OK;
            if (!isAlive) {
                logger.warn("python服务已经无法提供服务,port:" + port);
            }
            return isAlive;
        } catch (IOException e) {
            return false;
        }
    }

}
